#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

#define TAMANO_BUFFER_MAX 50
#define MAX_ITEM 100
#define MAX_RETARDO 3
#define MAX_THREAD 10

// el numero de iteraciones lo deje como variable 
// global y dentro del main hice 2 ciclos para
// generar los threads de productores y consumidores
// ademas para identificar los productores y los consumidores
// envio el identificador de cada productor y consumidor



sem_t vacio, lleno, mutex;
int n , idp, idc;
int buffer[TAMANO_BUFFER_MAX];
int in=0, out=0;
long num_iteraciones; //numero de iteraciones

void init(sem_t *sem, int valor_inicial) {
   if (sem_init(sem, 0, valor_inicial)) {
      printf("Error inicializando semaforo\n");
      exit(EXIT_FAILURE);
   }
}

void p(sem_t *sem) {
   if (sem_wait(sem)) {
      printf("Error fatal haciendo sem_wait()\n");
      exit(EXIT_FAILURE);
   }
}

void v(sem_t *sem) {
   if (sem_post(sem)) {
      printf("Error fatal haciendo sem_post()\n");
      exit(EXIT_FAILURE);
   }
}

void *productor(void *id) {
   int ide = (int )id;
   long i;

   for (i=0; i<num_iteraciones; i++) {
      sleep(rand()%MAX_RETARDO);   // Produce un item, con retardo aleatorio
      p(&vacio);
      p(&mutex);
      buffer[in] = rand()%MAX_ITEM;
      printf("productor(%d): produciendo en pos %d: %d\n",ide, in, buffer[in]);
      in = (in+1)%n;
      v(&mutex);
      v(&lleno);
   }
   pthread_exit(NULL);
}

void *consumidor(void *id) {
	
   int ide = (int)id; // numero de items
   
   long i;

   for (i=0; i<num_iteraciones; i++) {
      p(&lleno);
      p(&mutex);
      printf("consumidor(%d): consumiendo item %ld: %d\n",ide, i, buffer[out]);
      out = (out+1)%n;
      v(&mutex);
      v(&vacio);
      sleep(rand()%MAX_RETARDO);   // Consume el item, con retardo aleatorio
   }
   pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
   pthread_t prod[MAX_THREAD], cons[MAX_THREAD]; // hilos
   
   int num_productores;
   int num_consumidores;
   int i=0 , j=0; // contadores
   
   
   if (argc != 5) {
      printf("Uso correcto: %s tamano-buffer num-iteraciones num-productores num-comsumidores\n", argv[0]);
      exit(EXIT_FAILURE);
   }
   n = atoi(argv[1]);
   num_iteraciones = atoi(argv[2]);
   num_productores = atoi(argv[3]);
   num_consumidores = atoi(argv[4]);
   if (n > TAMANO_BUFFER_MAX) {
      printf("Tamano maximo de buffer: %d\n", TAMANO_BUFFER_MAX);
      exit(EXIT_FAILURE);
   }

   srand(time(NULL));

   init(&mutex, 1);
   init(&vacio, n);
   init(&lleno, 0);
	
	for(i = 0; i < num_productores; i++){ // numero de productores 
      printf("\t\t\tmain(): creando thread productor n: %d\n",i);
      if (pthread_create(&prod[i], NULL, productor, (void *)i)) {// se creas los hilos productores
         printf("Error creando thread productor\n");
         exit(EXIT_FAILURE);
      }
    }
	
   for(j=0; j < num_consumidores; j++){
      printf("\t\t\tmain(): creando thread consumidor n: %d\n",j);
      if (pthread_create(&cons[j], NULL, consumidor, (void *)j)) {// se crean los hilos consumidores
         printf("Error creando thread consumidor\n");
         exit(EXIT_FAILURE);
      }
   }

   pthread_exit(NULL);
}

